<template>
  <div class="rich-editor-container" :class="[themeClass, { disabled }]" :style="containerStyles">
    <!-- 工具栏 -->
    <div v-if="!readOnly" class="toolbar">
      <slot name="toolbar">
        <template v-for="btn in normalizedToolbar" :key="btn">
          <button v-if="typeof btn === 'string'"
                  @click="execCommand(btn)"
                  :class="{ active: isButtonActive(btn) }"
                  :title="getButtonTitle(btn)"
                  type="button"
                  :disabled="disabled">
            <span v-html="toolbarIcons[btn]"></span>
          </button>
          <slot v-else :name="btn.slotName"></slot>
        </template>
      </slot>
    </div>

    <!-- 编辑器内容 -->
    <div class="editor-content"
         ref="editor"
         :contenteditable="!readOnly && !disabled"
         @input="handleInput"
         @focus="handleFocus"
         @blur="handleBlur"
         @keydown="handleKeyDown"
         @compositionstart="isComposing = true"
         @compositionend="handleCompositionEnd"
         :style="contentStyles"
         :data-placeholder="placeholder"></div>

    <!-- 插槽扩展区域 -->
    <slot name="footer"></slot>
  </div>
</template>

<script setup>
import { ref, computed, watch, onMounted, nextTick, provide } from 'vue'
import DOMPurify from 'dompurify'
import { defaultToolbar, toolbarIcons, buttonTitles } from './toolbar'
import { useHistory } from './plugins'

const props = defineProps({
  modelValue: String,
  placeholder: { type: String, default: '' },
  defaultContent: { type: String, default: '' },
  toolbar: { type: Array, default: () => [...defaultToolbar] },
  height: { type: [String, Number], default: '300px' },
  width: { type: [String, Number], default: '100%' },
  readOnly: Boolean,
  disabled: Boolean,
  uploadConfig: {
    type: Object,
    default: () => ({
      // 必需参数
      url: '',         // 上传接口地址
      // 可选参数
      method: 'POST',  // 请求方法
      fieldName: 'file', // 文件字段名
      accept: 'image/*', // 可接受文件类型
      maxSize: null,    // 最大文件大小（字节）
      // 响应处理
      urlField: 'url',  // 响应数据中图片地址的字段名
      // 其他参数
      headers: {},      // 自定义请求头
      extraData: {}     // 额外提交参数
    })
  },
  theme: { type: String, default: 'light' },
  customStyle: { type: [String, Object], default: () => ({}) }
})

const emit = defineEmits([
  'update:modelValue',
  'change',
  'focus',
  'blur',
  'uploadSuccess',
  'uploadError'
])

// 编辑器实例和状态
const editor = ref(null)
const isComposing = ref(false)
const { undo, redo, recordState, canUndo, canRedo } = useHistory()

// 样式计算
const themeClass = computed(() => `theme-${props.theme}`)
const containerStyles = computed(() => ({
  width: typeof props.width === 'number' ? `${props.width}px` : props.width,
  ...(typeof props.customStyle === 'string' ? {} : props.customStyle)
}))

const contentStyles = computed(() => ({
  height: typeof props.height === 'number' ? `${props.height}px` : props.height
}))

// 工具栏处理
const normalizedToolbar = computed(() => {
  return props.toolbar.map(item => {
    if (typeof item === 'object') return item
    return defaultToolbar.includes(item) ? item : null
  }).filter(Boolean)
})

// 核心功能方法
const execCommand = (command, value = null) => {
  if (props.readOnly || props.disabled) return

  switch (command) {
    case 'image':
      handleImageUpload()
      break
    case 'undo':
      undoCommand()
      break
    case 'redo':
      redoCommand()
      break
    default:
      document.execCommand(command, false, value)
      editor.value.focus()
      triggerChange()
  }
}

const handleInput = (e) => {
  if (isComposing.value) return
  triggerChange()
}

const triggerChange = () => {
  nextTick(() => {
    const cleanHTML = sanitizeContent(editor.value.innerHTML)
    emit('update:modelValue', cleanHTML)
    emit('change', cleanHTML)
    recordState(cleanHTML)
  })
}

// 安全处理
const sanitizeContent = (dirty) => {
  return DOMPurify.sanitize(dirty, {
    ALLOWED_TAGS: ['p', 'b', 'i', 'u', 'em', 'strong', 'a', 'img', 'h1', 'h2', 'h3', 'br', 'ul', 'ol', 'li'],
    ALLOWED_ATTR: ['href', 'target', 'src', 'alt', 'style']
  })
}

// 图片上传处理
const handleImageUpload = () => {
  if (!props.uploadConfig?.url) {
    console.error('未配置上传地址')
    emit('uploadError', {
      error: new Error('请配置上传地址'),
      file: null
    })
    return
  }

  const input = document.createElement('input')
  input.type = 'file'
  input.accept = props.uploadConfig.accept || 'image/*'

  input.onchange = async (e) => {
    const file = e.target.files[0]
    if (!file) return

    // 文件验证
    if (props.uploadConfig.maxSize && file.size > props.uploadConfig.maxSize) {
      const error = new Error(`文件大小超过限制（最大${props.uploadConfig.maxSize/1024/1024}MB）`)
      emit('uploadError', { error, file })
      return
    }

    try {
      const formData = new FormData()
      const fieldName = props.uploadConfig.fieldName || 'file'
      formData.append(fieldName, file)

      // 添加额外参数
      if (props.uploadConfig.extraData) {
        Object.entries(props.uploadConfig.extraData).forEach(([key, value]) => {
          formData.append(key, value)
        })
      }

      const response = await fetch(props.uploadConfig.url, {
        method: props.uploadConfig.method || 'POST',
        headers: {
          'Accept': 'application/json',
          ...props.uploadConfig.headers
        },
        body: formData
      })

      if (!response.ok) {
        throw new Error(`上传失败：${response.status} ${response.statusText}`)
      }

      const data = await response.json()

      // 处理响应数据
      const urlPath = props.uploadConfig.urlField
          ? data[props.uploadConfig.urlField]
          : data.url

      if (!urlPath) {
        throw new Error('服务器响应缺少文件地址')
      }

      execCommand('insertImage', urlPath)
      emit('uploadSuccess', { response: data, file })
    } catch (error) {
      console.error('上传失败:', error)
      emit('uploadError', { error, file })
    }
  }

  input.click()
}

// 历史记录管理
const undoCommand = () => {
  if (canUndo.value) {
    editor.value.innerHTML = undo()
    triggerChange()
  }
}

const redoCommand = () => {
  if (canRedo.value) {
    editor.value.innerHTML = redo()
    triggerChange()
  }
}

// 初始化内容
const initContent = () => {
  const content = props.modelValue || props.defaultContent
  if (editor.value) {
    editor.value.innerHTML = sanitizeContent(content)
    recordState(content)
  }
}

// 事件处理
const handleFocus = (e) => {
  emit('focus', e)
}

const handleBlur = (e) => {
  emit('blur', e)
}

const handleCompositionEnd = () => {
  isComposing.value = false
  triggerChange()
}

// 快捷键支持
const handleKeyDown = (e) => {
  // 组合键处理
  if (e.ctrlKey || e.metaKey) {
    switch (e.key.toLowerCase()) {
      case 'b':
        e.preventDefault()
        execCommand('bold')
        break
      case 'i':
        e.preventDefault()
        execCommand('italic')
        break
      case 'z':
        e.preventDefault()
        undoCommand()
        break
      case 'y':
        e.preventDefault()
        redoCommand()
        break
    }
  }
}

// 按钮状态检测
const isButtonActive = (command) => {
  if (command === 'undo') return canUndo.value
  if (command === 'redo') return canRedo.value
  return document.queryCommandState(command)
}

const getButtonTitle = (command) => {
  return buttonTitles[command] || command
}

// 监听外部内容变化
watch(() => props.modelValue, (newVal) => {
  if (newVal !== editor.value.innerHTML) {
    editor.value.innerHTML = sanitizeContent(newVal)
  }
})

// 生命周期
onMounted(() => {
  initContent()
  provide('editorAPI', {
    execCommand,
    getContent: () => sanitizeContent(editor.value.innerHTML),
    setContent: (content) => {
      editor.value.innerHTML = sanitizeContent(content)
      triggerChange()
    }
  })
})
</script>

<style src="./style.css"></style>